package model

import (
	"errors"

	"github.com/quarkcloudio/quark-go/v2/pkg/dal/db"
	sqlManager "github.com/wsl/hi-go/plugins/sql"
	"github.com/wsl/hi-go/types"
	"gorm.io/gorm"
)

type Student struct {
	Id        int              `json:"id" gorm:"autoIncrement"`
	Name      string           `json:"name" gorm:"type:varchar(500)" binding:"required" form:"name" query:"name"`
	Status    int              `json:"status" gorm:"default:1"`
	Age       int              `json:"age" binding:"required" form:"age" query:"age"`
	ClassId   *int             `json:"classId" form:"classId" query:"classId"`
	CreatedAt *types.LocalTime `json:"created_at"`
	UpdatedAt *types.LocalTime `json:"updated_at"`
	DeletedAt gorm.DeletedAt   `json:"-"`
}

func (p *Student) Create(name string, age int, classId int) error {
	student := Student{Name: name, Age: age, ClassId: &classId}
	dbErr := db.Client.Model(&p).Create(&student).Error
	return dbErr
}

type StudentResult struct {
	Student
	ClassesName string `json:"classesName" gorm:"column:classesName"`
}

func (p *Student) Index(page int, pageSize int) (sqlManager.AutoList[*StudentResult], error) {
	students := make([]*StudentResult, 0)
	// Scopes 公用代码块
	query := db.Client.Model(&p)
	var count int64 = 0
	// 获取总数
	if totalErr := query.Count(&count).Error; totalErr != nil {
		return sqlManager.AutoList[*StudentResult]{List: students, Total: count}, totalErr
	}
	err := query.Scopes(sqlManager.PageManager(page, pageSize)).Unscoped().Select("students.*, classes.name as classesName").
		Joins("left join classes on students.class_id = classes.id").Scan(&students).Error
	return sqlManager.AutoList[*StudentResult]{List: students, Total: count}, err
}

func (p *Student) BindStudentToClass(updateStudentClassParam types.UpdateStudentClassParam) error {
	tx := db.Client.Begin()
	var err error
	defer func() {
		if err != nil {
			tx.Rollback()
		}
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()
	var student Student
	// 这里锁行了，必须保证错误的时候回滚或者正确提交事务
	// 查找学生
	err = tx.Where("id = ?", updateStudentClassParam.StudentId).First(&student).Set(sqlManager.QueryOptionForUpdate.Key, sqlManager.QueryOptionForUpdate.Value).Error
	if err != nil {
		return errors.New("学生查询失败，请检查学生id是否正确")
	}
	if *student.ClassId == updateStudentClassParam.ClassId {
		return errors.New("请勿重复绑定")
	}

	// 更新班级
	if student.ClassId == nil {
		var newClass Class
		// 没有原来班级
		err = tx.Where("id = ?", updateStudentClassParam.ClassId).First(&newClass).Set(sqlManager.QueryOptionForUpdate.Key, sqlManager.QueryOptionForUpdate.Key).Error
		if err != nil {
			return errors.New("新班级查询失败，请检查新班级id是否正确")
		}
		err = tx.Where("id = ?", updateStudentClassParam.ClassId).First(&newClass).Update("StudentNum", gorm.Expr("StudentNum + 1")).Set(sqlManager.QueryOptionForUpdate.Key, sqlManager.QueryOptionForUpdate.Key).Error
		if err != nil {
			return errors.New("新班级更新失败")
		}
	} else {
		// 存在原来班级
		var newClass, oldClass Class
		var classes []Class
		err = tx.Where("id in (?)", []int{updateStudentClassParam.ClassId, *student.ClassId}).Set(sqlManager.QueryOptionForUpdate.Key, sqlManager.QueryOptionForUpdate.Value).Find(&classes).Error
		if err != nil {
			return err
		}
		for _, class := range classes {
			if class.Id == updateStudentClassParam.ClassId {
				newClass = class
			} else if class.Id == *student.ClassId {
				oldClass = class
			}
		}

		err = tx.Exec(`
		update classes
		set student_num = CASE 
		    WHEN id = ? THEN ?
		    WHEN id = ? THEN ?
		END
		WHERE id in (?,?)`, oldClass.Id, oldClass.StudentNum-1, newClass.Id, newClass.StudentNum+1, oldClass.Id, newClass.Id).Error
		if err != nil {
			return err
		}
	}

	updateStudent := Student{
		ClassId: &updateStudentClassParam.ClassId,
	}
	// 更新学生
	err = tx.Model(&student).Updates(&updateStudent).Error
	if err != nil {
		return errors.New("更新班级失败")
	}

	return tx.Commit().Error
}
